package user

import (
	"errors"
	"fmt"
	uuid "github.com/satori/go.uuid"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo/options"
	"higame/config"
	"higame/modules/crypto"
	mongodb "higame/modules/db"
	"higame/modules/role"
	"higame/modules/utils"
)

var db = mongodb.GetInstance()
var colName = config.Db.Collection.User

type AddressSchema struct {
	UUID     string `json:"uuid" bson:"uuid" form:"uuid"`
	Address  string `json:"address" bson:"address" form:"address" binding:"required"`
	Tel      string `json:"tel" bson:"tel" form:"tel" binding:"required"`
	Name     string `json:"name" bson:"name" form:"name" binding:"required"`
	PostCode string `json:"post_code" bson:"post_code" form:"post_code" binding:"required"`
}

type Schema struct {
	ID            primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"`
	Username      string             `json:"username" bson:"username" form:"username" binding:"required,min=3,max=8"`
	Password      string             `json:"password" bson:"password" form:"password" binding:"required,min=5,max=20"`
	Avatar        string             `json:"avatar" bson:"avatar" form:"avatar" binding:""`
	Nick          string             `json:"nick" bson:"nick" form:"nick" binding:"required"`
	Role          role.Type          `json:"role" bson:"role" form:"role"`
	ShareCode     string             `json:"share_code" bson:"share_code" form:"share_code" binding:"required"`
	UUID          string             `json:"uuid" bson:"uuid" form:"uuid"`
	EXP           int64              `json:"exp" bson:"exp" form:"exp"`
	Integral      int64              `json:"integral" bson:"integral" form:"integral"`
	Email         string             `json:"email" bson:"email" form:"email" binding:"required,email"`
	IsLock        bool               `json:"is_lock" bson:"is_lock" form:"is_lock"`
	QQ            string             `json:"qq" bson:"qq" form:"qq" binding:""`
	WX            string             `json:"wx" bson:"wx" form:"wx" binding:""`
	Alipay        string             `json:"alipay" bson:"alipay" form:"alipay" binding:""`
	Tel           string             `json:"tel" bson:"tel" form:"tel" binding:""`
	Tag           string             `json:"tag" bson:"tag" form:"tag" binding:""`
	BdInfo        string             `json:"bd_info" bson:"bd_info" form:"bd_info" binding:""`
	AddTime       int64              `json:"add_time" bson:"add_time" form:"add_time"`
	EndTime       int64              `json:"end_time" bson:"end_time" form:"end_time"`
	PcCode        string             `json:"pc_code" bson:"pc_code" form:"pc_code" binding:"required"`
	ClientID      string             `json:"client_id" bson:"client_id" form:"client_id" binding:"required"`
	LastLoginTime int64              `json:"last_login_time" bson:"last_login_time" form:"last_login_time" binding:""`
	LastCheckTime int64              `json:"last_check_time" bson:"last_check_time" form:"last_check_time" binding:""`
	Address       []AddressSchema    `json:"address" bson:"address" form:"address" binding:""`
}

func New() *Schema {
	return &Schema{}
}

func Parse(data bson.M) (*Schema, error) {
	u := New()
	if err := db.ToStruct(data, u); err != nil {
		return nil, err
	}
	return u, nil
}

func DeleteOneByName(name string) error {
	b := db.DeleteOne(colName, bson.M{"username": name})
	if !b {
		return errors.New("删除失败")
	}
	return nil
}

func AddOne(userSchema *Schema) error {
	adminI := db.InsertOne(config.Db.Collection.User, userSchema)
	if !adminI {
		return fmt.Errorf("添加失败。\n")
	}
	return nil
}

func FindOneByName(name string) (*Schema, error) {
	userF := db.FindOne(colName, bson.M{"username": name})
	if userF == nil {
		return nil, errors.New("用户不存在！")
	}

	user, err := Parse(userF)
	return user, err
}

func FindOne(filer interface{}) (*Schema, error) {
	userF := db.FindOne(colName, filer)
	if userF == nil {
		return nil, errors.New("用数据不存在！")
	}
	user, err := Parse(userF)
	return user, err
}

func FindAll(pageNum int64, filter interface{}) ([]bson.M, error) {
	if pageNum < 0 {
		pageNum = 0
	}
	opts := new(options.FindOptions)
	list := db.FindMany(colName, filter,
		opts.SetSkip(pageNum*20),
		opts.SetLimit(20),
		opts.SetSort(bson.M{
			"add_time": 1,
		}))
	return list, nil
}

func UpdateOneByName(name string, update interface{}) error {
	b := db.UpdateOne(colName, bson.M{
		"username": name,
	}, bson.M{
		"$set": update,
	})

	if !b {
		return errors.New("更新用户信息失败")
	}
	return nil
}

func UpdateOne(filter interface{}, update interface{}) error {
	b := db.UpdateOne(colName, filter, update)

	if !b {
		return errors.New("更新用户信息失败")
	}
	return nil
}

func AddOrResetAdmin() error {
	username := "admin"
	_, err := FindOneByName(username)
	if err != nil {
		return AddOne(&Schema{
			Username:      "admin",
			Password:      crypto.Md5encrypt("admin"),
			Nick:          "Admin",
			UUID:          uuid.NewV4().String(),
			Role:          role.ADMIN,
			ShareCode:     utils.MakeKey(6),
			Integral:      0,
			Email:         "igufei.126.com",
			IsLock:        false,
			QQ:            "",
			WX:            "",
			Tel:           "",
			Tag:           "",
			BdInfo:        "",
			AddTime:       utils.Timestamp(),
			EndTime:       -1,
			PcCode:        "",
			ClientID:      "",
			LastLoginTime: -1,
			LastCheckTime: -1,
			Address:       []AddressSchema{},
		})
	} else {
		return UpdateOneByName(username, bson.M{
			"password": crypto.Md5encrypt("admin"),
		})
	}

}

func Count(filter interface{}) (int64, error) {
	return db.Count(colName, filter), nil
}

func GetAddressByName(name string) ([]AddressSchema, error) {
	u, err := FindOneByName(name)
	if err != nil {
		return nil, err
	}
	return u.Address, nil
}
